/* Copyright   1991 Gustavus Adolphus College.  All rights reserved.
 *
 * Schematik was developed by Gustavus Adolphus College (GAC) with
 * support from NeXT Computer, Inc.  Permission to copy this software,
 * to redistribute it, and to use it for any purpose is granted,
 * subject to the following restrictions and understandings.
 *
 * 1. Any copy made of this software must include this copyright
 * notice in full.
 *
 * 2. Users of this software agree to make their best efforts (a) to
 * return to the GAC Mathematics and Computer Science Department any
 * improvements or extensions that they make, so that these may be
 * included in future releases; and (b) to inform GAC of noteworthy
 * uses of this software.
 *
 * 3. All materials developed as a consequence of the use of this
 * software shall duly acknowledge such use, in accordance with the
 * usual standards of acknowledging credit in academic research.
 *
 * 4. GAC makes no express or implied warranty or representation of
 * any kind with respect to this software, including any warranty
 * that the operation of this software will be error-free.  ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
 * PURPOSE IS HEREBY DISCLAIMED.  GAC is under no obligation to
 * provide any services, by way of maintenance, update, or otherwise.
 *
 * 5. In conjunction with products arising from the use of this
 * material, there shall be no use of the name of Gustavus Adolphus
 * College nor of any adaptation thereof in any advertising,
 * promotional, or sales literature without prior written consent
 * from GAC in each case.
 */

#import "PrefAgent.h"
#import "../defines.h"
#import Main_h
#import <appkit/ButtonCell.h>
#import <appkit/Form.h>
#import <appkit/NXBrowser.h>
#import <appkit/NXBrowserCell.h>
#import <appkit/NXImage.h>
#import <appkit/Text.h>
#import <appkit/defaults.h>
#import <objc/List.h>
#import <objc/NXStringTable.h>
#import <sys/fcntl.h>
#import <libc.h>
#import <mach.h>

#define GetBOOLValue(x)	(NXOrderStrings((unsigned char *)defaults[x].value, (unsigned char *)"YES", YES, -1, NULL)?NO:YES)

#define NIBFILE		"Preferences.nib"
#define BUTTONTIFF	"Pref"
#define BUTTONTIFFH	"PrefH"
#define TIFFWIDTH	64
#define PREFZONENAME	"PrefAgnt"
#define NATIVE_LANGUAGE	"English"
#define LANGUAGEDIR	"%s/%s.lproj/%s"
#define LANGUAGESTRINGS	"%s/%s.lproj/%s.strings"
#define LANGUAGETIFFS	"%s/%s.lproj/%s.tiff"
#define NUMBOXWINDOWS	4
#define REVERTBUTTON	0
#define CLOSEBUTTON	1
#define OKBUTTON	2
#define PREFABEXEC	"%s/kernel/scheme"
#define PREFABPARAMS	"-emacs -library %s/kernel/library %s %s"
#define SICPBAND	"-band sicp.com"
#define MITCOMPILER	"-compiler"

#define FORMTBL	"{begin,0}{call-with-input-file,1}{call-with-output-file,1}{case,1}{define,1}{define-integrable,1}{define-macro,1}{define-structure,1}{define-syntax,1}{delay,0}{do,2}{fluid-let,1}{in-package,1}{lambda,1}{let,1}{let*,1}{letrec,1}{let-syntax,1}{list-search-negative,1}{list-search-positive,1}{list-transform-negative,1}{list-transform-positive,1}{local-declare,1}{macro,1}{make-environment,0}{named-lambda,1}{pathname-components,1}{sequence,0}{syntax-table-define,2}{using-syntax,1}{with-input-from-file,1}{with-input-from-string,1}{with-output-to-file,1}{with-output-to-port,1}{with-output-to-string,0}{with-values,1}"

#define DEFLTS	{{"App_ConfirmOnClose","YES"}, {"App_ConfirmOnQuit","YES"}, {"App_ErrorAbort","1"}, {"Document_Offset","{20.0,-20.0}"}, {"Document_Pos","{280.0,70.0}"}, {"Interaction_Autoscroll","YES"}, {"Interaction_CommentOutput","NO"}, {"Interaction_EvaluateAll","NO"}, {"Interaction_Font","Ohlfs"}, {"Interaction_FontSize","11.0"}, {"Interaction_InsertErrors","YES"}, {"Interaction_Frame","{{240.0,400.0},{560.0,400.0}}"}, {"Scheme_Dialect","0"}, {"Scheme_Executable","/usr/local/bin/scheme"}, {"Scheme_Parameters","-emacs"}, {"Scheme_SICPParameters"," "}, {"Scheme_MITParameters"," "}, {"Scheme_SpecialForms", FORMTBL}, {"Text_Autoindent","YES"}, {"Text_DoubleClickMatch","YES"}, {"Text_FormatAfterPaste","YES"}, {"Text_MatchParenthesis","YES"}, {"Interaction_Transcript","NO"}, {"Text_ReverseVideo","NO"}, {"App_UseSpecialCursors","NO"}, {"Graphics_Size","{400.0,300.0}"}, {"Graphics_SizeTolerance","{112.0,90.0}"}, {"App_KeepBackupCopy","YES"}, {NULL,NULL}}

#define CONFIRMONCLOSE	0
#define CONFIRMONQUIT	1
#define ERRORABORT	2
#define DOCOFFSET	3
#define DOCPOS		4
#define AUTOSCROLL	5
#define COMMENTOUTPUT	6
#define EVALUATEALL	7
#define INTERFONT	8
#define INTERFONTSIZE	9
#define INSERTERRORS	10
#define INTERFRAME	11
#define SCHEMEDIALECT	12
#define EXECUTABLE	13
#define PARAMETERS	14
#define SICPPARAMETERS	15
#define MITPARAMETERS	16
#define SPECIALFORMS	17
#define AUTOINDENT	18
#define DBLCLICKMATCH	19
#define FRMTAFTRPASTE	20
#define MATCHPARENS	21
#define TRANSCRIPT	22
#define REVERSEVIDEO	23
#define SPECIALCURSORS	24
#define GRAPHICSSIZE	25
#define SIZETOLERANCE	26
#define KEEPBACKUPCOPY	27
#define NUMDEFAULTS	28

@interface PrefAgent (Private)

- _revertDefaults;
- _setDefaults;
- _setDialectOpts:(int)dialect;
- _specialFormOpts:(int)buttontag;
- _copyKeywordList;

@end

static Window *win[NUMBOXWINDOWS];
static NXImage *tiff=NULL;
static NXImage *tiffH=NULL;
static struct _NXDefault defaults[NUMDEFAULTS+1];
static const char *const *languages;
static NXStringTable *stringTable=nil;
static List *keywordList=nil,*oldKeywordList=nil;
static int keywordListVersion=0;
static id theAgent=nil;

@implementation PrefAgent

+ new
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (theAgent==nil)
      {
        NXZone *tempZone;
	extern char *appDirectory;
	const char *appName=[NXApp appName];
	struct _NXDefault defaultdefaults[]=DEFLTS;
	char *temp,*tempdef;
	int i;
    
	DEBUG_ASSERT((appName!=NULL)&&(*appName))
        tempZone = NXCreateZone(vm_page_size, vm_page_size, YES);
        DEBUG_ASSERT(tempZone!=NULL)
        theAgent = self = [super allocFromZone:tempZone];
	agentZone = tempZone;
	NXNameZone(agentZone, PREFZONENAME);
	#ifdef DEBUG
	  DEBUG_ASSERT(NXRegisterDefaults(appName, defaultdefaults))
	#else
	  NXRegisterDefaults(appName, defaultdefaults);
	#endif
	for(i=0; i<NUMDEFAULTS; i++)
	  {
	    defaults[i].name = NXCopyStringBufferFromZone(defaultdefaults[i].name, agentZone);
	    DEBUG_ASSERT((defaults[i].name!=NULL)&&(*defaults[i].name))
	    DEBUG_ASSERT(NXGetDefaultValue(appName, defaultdefaults[i].name))
	    defaults[i].value = NXCopyStringBufferFromZone(NXGetDefaultValue(appName, defaultdefaults[i].name), agentZone);
	    DEBUG_ASSERT((defaults[i].value!=NULL)&&(*defaults[i].value))
	  }
	defaults[NUMDEFAULTS].name = NULL;
	defaults[NUMDEFAULTS].value = NULL;
	stringTable = [[NXStringTable allocFromZone:agentZone] init];
	DEBUG_ASSERT(stringTable!=nil)
	languages = [NXApp systemLanguages];
	if (languages)
	  for(i=0; *(languages+i); i++)
	    {
	      char path[strlen(LANGUAGESTRINGS)+2*strlen(*(languages+i))+strlen(appDirectory)];

	      if (!NXOrderStrings((unsigned char *)*languages, (unsigned char *)NATIVE_LANGUAGE, YES, -1, NULL)) break;
	      sprintf(path, LANGUAGESTRINGS, appDirectory, *(languages+i), *(languages+i));
	      if (!access(path, R_OK))
		{
		  #ifdef DEBUG
		    DEBUG_ASSERT([stringTable readFromFile:path])
		  #else
		    [stringTable readFromFile:path];
		  #endif
		  break;
		}
	    }
	keywordList = [[List allocFromZone:agentZone] init];
	DEBUG_ASSERT(keywordList!=nil)
	tempdef = NXCopyStringBuffer(defaults[SPECIALFORMS].value);
	DEBUG_ASSERT((tempdef!=NULL)&&(*tempdef))
	temp = strtok(tempdef,"{}");
	for(i=0; ((temp!=NULL)&&(*temp)); i++)
	  {
	    char tempkeyword[128];
	    int tempde;
    
	    sscanf(temp, "%[^,],%i", tempkeyword, &tempde);
	    DEBUG_ASSERT((tempkeyword!=NULL)&&(*tempkeyword))
	    [keywordList addObject:[[Keyword allocFromZone:agentZone] initTo:tempkeyword with:tempde]];
	    temp = strtok(NULL,"{}");
	  }
	oldKeywordList = [self _copyKeywordList];
	if (tempdef!=NULL) free(tempdef);
      }
    return theAgent;
}

- activateAgent:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (!prefPanel)
      {
        NXRect r={{0,0},{TIFFWIDTH,TIFFWIDTH}};
        int i;

        prefPanel = LoadNIB(NIBFILE, theAgent, agentZone);
        DEBUG_ASSERT(prefPanel!=nil)
        [prefPanel useOptimizedDrawing:YES];
        win[CLIENT0] = win0;
        win[CLIENT1] = win1;
        win[CLIENT2] = win2;
        win[CLIENT3] = win3;
        for(i=0; i<NUMBOXWINDOWS; i++)
          {
            DEBUG_ASSERT(win[i]!=nil)
            [win[i] useOptimizedDrawing:YES];
          }
        [theBox setContentView:[win[CLIENT0] contentView]];
        DEBUG_ASSERT([theBox contentView]!=nil)
        tiff = [[NXApp prefAgent] localImage:BUTTONTIFF fromZone:agentZone];
        DEBUG_ASSERT(tiff!=nil)
        tiffH = [[NXApp prefAgent] localImage:BUTTONTIFFH fromZone:agentZone];
        DEBUG_ASSERT(tiffH!=nil)
        for(i=0; i<NUMBOXWINDOWS; i++)
          {
            r.origin.x = i*TIFFWIDTH;
            DEBUG_ASSERT([buttonMatrix cellAt:0 :i]!=nil)
            [[buttonMatrix cellAt:0 :i] setImage:[[NXImage allocFromZone:agentZone] initFromImage:tiff rect:&r]];
            DEBUG_ASSERT([[buttonMatrix cellAt:0 :i] image]!=nil)
            [[buttonMatrix cellAt:0 :i] setAltImage:[[NXImage allocFromZone:agentZone] initFromImage:tiffH rect:&r]];
            DEBUG_ASSERT([[buttonMatrix cellAt:0 :i] altImage]!=nil)
          }
      }
    [self _revertDefaults];
    [prefPanel makeKeyAndOrderFront:self];
    return self;
}

- buttonClicked:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    switch ([[sender selectedCell] tag])
      {
        case REVERTBUTTON: return [self _revertDefaults];
        case CLOSEBUTTON : return [prefPanel performClose:self];
        case OKBUTTON    : return [self _setDefaults];
      }
    DEBUG_ASSERT(NO /* should not get here*/)
    return self;
}

- selectPrefs:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    [theBox setContentView:[win[[[sender selectedCell] tag]] contentView]];
    DEBUG_ASSERT([theBox contentView]!=nil)
    return [theBox display];
}

@end

@implementation PrefAgent (Delegate)

- windowWillClose:sender
{
    int i;

DEBUG_FUNC1(DEBUGLEVEL);
    for(i=0; i<NUMBOXWINDOWS; i++)
      {
        win[i] = [win[i] close];
        DEBUG_ASSERT(win[i]==nil)
      }
    [theBox setContentView:nil];
    for(i=0; i<NUMBOXWINDOWS; i++)
      {
        [[[buttonMatrix cellAt:0 :i] image] free];
        [[[buttonMatrix cellAt:0 :i] altImage] free];
      }
    [tiff free];
    [tiffH free];
    prefPanel = nil;
    return self;
}

@end

@implementation PrefAgent (Localization)

- loadLocalNib:(const char *)nibFile owner:(id)owner fromZone:(NXZone *)zone
{
    extern char *appDirectory;
    id retval=nil;
    int i;

DEBUG_FUNC1(DEBUGLEVEL);
    if (languages)
      {
        for(i=0; *(languages+i); i++)
          {
            char path[strlen(LANGUAGEDIR)+strlen(*languages)+strlen(appDirectory)+strlen(nibFile)];

            if (!NXOrderStrings((unsigned char *)*languages, (unsigned char *)NATIVE_LANGUAGE, YES, -1, NULL)) break;
            sprintf(path, LANGUAGEDIR, appDirectory, *(languages+i), nibFile);
            if (!access(path, R_OK))
              {
                retval = [NXApp loadNibFile:path owner:owner withNames:NO fromZone:zone];
                DEBUG_ASSERT(retval!=nil)
                break;
              }
          }
      }
    return (retval?:[NXApp loadNibSection:nibFile owner:owner withNames:NO fromZone:zone]);
}

- loadLocalMainMenu:(const char *)nibFile
{
    extern char *appDirectory;
    char bpath[strlen(LANGUAGEDIR)+strlen(NATIVE_LANGUAGE)+strlen(appDirectory)+strlen(nibFile)];
    int i;

DEBUG_FUNC1(DEBUGLEVEL);
    if (languages)
      for(i=0; *(languages+i); i++)
	{
	  char path[strlen(LANGUAGEDIR)+strlen(*(languages+i))+strlen(appDirectory)+strlen(nibFile)];
	  sprintf(path, LANGUAGEDIR, appDirectory, *(languages+i), nibFile);
	  if (!access(path, R_OK))
	    return [NXApp loadNibFile:path owner:NXApp withNames:NO fromZone:[NXApp zone]];
	}
    sprintf(bpath, LANGUAGEDIR, appDirectory, NATIVE_LANGUAGE, nibFile);
    return [NXApp loadNibFile:bpath owner:NXApp withNames:NO fromZone:[NXApp zone]];
}

- localImage:(const char *)imageFile fromZone:(NXZone *)zone
{
    extern char *appDirectory;
    id retval=nil;
    int i;

DEBUG_FUNC1(DEBUGLEVEL);
    if (languages)
      {
        for(i=0; *(languages+i); i++)
          {
            char path[strlen(LANGUAGEDIR)+strlen(*languages)+strlen(appDirectory)+strlen(imageFile)];

            if (!NXOrderStrings((unsigned char *)*languages, (unsigned char *)NATIVE_LANGUAGE, YES, -1, NULL)) break;
            sprintf(path, LANGUAGETIFFS, appDirectory, *(languages+i), imageFile);
            if (!access(path, R_OK))
              {
                retval = [[NXImage allocFromZone:zone] initFromFile:path];
                DEBUG_ASSERT(retval!=nil)
                break;
              }
          }
      }
    return (retval?:[[NXImage allocFromZone:zone] initFromSection:imageFile]);
}

const char *doLocalString(const char *key, const char *value)
{
    return ([stringTable valueForStringKey:key]?:(value?:key));
}

@end

@implementation PrefAgent (Client0)

- (BOOL)evaluateAll
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(EVALUATEALL);
}

- (BOOL)commentOutput
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(COMMENTOUTPUT);
}

- (BOOL)insertErrors
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(INSERTERRORS);
}

- (BOOL)transcriptMode
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(TRANSCRIPT);
}

- (BOOL)autoScroll
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(AUTOSCROLL);
}

- (BOOL)confirmOnClose
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(CONFIRMONCLOSE);
}

- setConfirmOnClose:(BOOL)aBool
{
    NXSetDefault([NXApp appName], defaults[CONFIRMONCLOSE].name, (aBool?"YES":"NO"));
    return self;
}

- (BOOL)confirmOnQuit
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(CONFIRMONQUIT);
}

- (BOOL)keepBackupCopy
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(KEEPBACKUPCOPY);
}

- (int)errorAbort
{
DEBUG_FUNC1(DEBUGLEVEL);
    return atoi(defaults[ERRORABORT].value);
}

@end

@implementation PrefAgent (Client1)

- (BOOL)dblClickMatch
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(DBLCLICKMATCH);
}

- (BOOL)autoindent
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(AUTOINDENT);
}

- (BOOL)matchParens
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(MATCHPARENS);
}

- (BOOL)formatAfterPaste
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(FRMTAFTRPASTE);
}

@end

@implementation PrefAgent (Client2)

- formsButtonClicked:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [self _specialFormOpts:[[sender selectedCell] tag]];
}

- formsBrowserClick:sender
{
    int row=[[formsBrowser matrixInColumn:BROWSERCOLUMN] selectedRow];
DEBUG_FUNC1(DEBUGLEVEL);
    [[formsField setStringValue:[[keywordList objectAt:row] keyword] at:0] selectTextAt:0];
    [deField setIntValue:[[keywordList objectAt:row] distinguishedElems]];
    return self;
}

- (BOOL)isSpecialForm:(const char *)aString
{
    int i;

DEBUG_FUNC1(DEBUGLEVEL);
    for(i=0; i<[keywordList count]; i++)
      if (!NXOrderStrings((unsigned char *)aString, (unsigned char *)[[keywordList objectAt:i] keyword], NO, -1, NULL))
        return YES;
    return NO;
}

- (int)deForSpecialForm:(const char *)aString
{
    int i;

DEBUG_FUNC1(DEBUGLEVEL);
    for(i=0; i<[keywordList count]; i++)
      if (!NXOrderStrings((unsigned char *)aString, (unsigned char *)[[keywordList objectAt:i] keyword], NO, -1, NULL))
        return [[keywordList objectAt:i] distinguishedElems];
    return -1;
}

- (int)keywordListVersion
{
DEBUG_FUNC1(DEBUGLEVEL);
    return keywordListVersion;
}

@end

@implementation PrefAgent (Client3)

- dialectChosen:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [self _setDialectOpts:[[sender selectedCell] tag]];
}

- (int)schemeDialect
{
DEBUG_FUNC1(DEBUGLEVEL);
    return atoi(defaults[SCHEMEDIALECT].value);
}

- (char *)executable
{
    extern char *appDirectory;

DEBUG_FUNC1(DEBUGLEVEL);
    switch ([self schemeDialect])
      {
        case SICPDIALECT   :
        case MITDIALECT    :
        case MITDIALECTCOMP: {
			       char *executable=(char *)malloc(strlen(PREFABEXEC)+strlen(appDirectory));
			       DEBUG_ASSERT(executable!=NULL)
			       sprintf(executable, PREFABEXEC, appDirectory); 
			       return executable;
			     }
        case OTHERDIALECT: {
                             char *executable;
                             executable = NXCopyStringBuffer(defaults[EXECUTABLE].value);
                             DEBUG_ASSERT(executable!=NULL)
                             return executable;
                           }
      }
    DEBUG_ASSERT(NO /* should not get here */)
    return NULL;
}

- (char *)parameters
{
    extern char *appDirectory;

DEBUG_FUNC1(DEBUGLEVEL);
    switch ([self schemeDialect])
      {
        case SICPDIALECT : {
                             char *parameters=(char *)malloc(strlen(PREFABPARAMS)+strlen(appDirectory)+
                                                       strlen(SICPBAND)+strlen(defaults[SICPPARAMETERS].value));
                             DEBUG_ASSERT(parameters!=NULL)
                             sprintf(parameters, PREFABPARAMS, appDirectory, SICPBAND, defaults[SICPPARAMETERS].value);
                             return parameters;
                           }
        case MITDIALECT  : {
                             char *parameters=(char *)malloc(strlen(PREFABPARAMS)+strlen(appDirectory)+
			                                      strlen(defaults[MITPARAMETERS].value));
                             DEBUG_ASSERT(parameters!=NULL)
                             sprintf(parameters, PREFABPARAMS, appDirectory, "", defaults[MITPARAMETERS].value);
                             return parameters;
                           }
        case MITDIALECTCOMP: {
			       char *parameters=(char *)malloc(strlen(PREFABPARAMS)+strlen(appDirectory)+
				 			    strlen(MITCOMPILER)+strlen(defaults[MITPARAMETERS].value));
			       DEBUG_ASSERT(parameters!=NULL)
			       sprintf(parameters, PREFABPARAMS, appDirectory, MITCOMPILER, defaults[MITPARAMETERS].value);
			       return parameters;
                             }
        case OTHERDIALECT: {
                             char *parameters;
                             parameters = NXCopyStringBuffer(defaults[PARAMETERS].value);
                             DEBUG_ASSERT(parameters!=NULL)
                             return parameters;
                           }
      }
    DEBUG_ASSERT(NO /* should not get here */)
    return NULL;
}

@end

@implementation PrefAgent (NoClient)

- setInterFrame:(const NXRect *)r
{
    char buffer[64];

DEBUG_FUNC1(DEBUGLEVEL);
    sprintf(buffer, "{{%f,%f},{%f,%f}}", r->origin.x, r->origin.y, r->size.width, r->size.height);
    NXZoneFree(agentZone, defaults[INTERFRAME].value);
    defaults[INTERFRAME].value = NXCopyStringBufferFromZone(buffer, agentZone);
    DEBUG_ASSERT(defaults[INTERFRAME].value!=NULL)
    NXWriteDefaults([NXApp appName], defaults);
    return self;
}

- getInterFrame:(NXRect *)r
{
DEBUG_FUNC1(DEBUGLEVEL);
    sscanf(defaults[INTERFRAME].value, "{{%f,%f},{%f,%f}}", &r->origin.x, &r->origin.y, &r->size.width, &r->size.height);
    return self;
}

- setInterFont:(id)aFont
{
    char buffer[16];

DEBUG_FUNC1(DEBUGLEVEL);
    NXZoneFree(agentZone, defaults[INTERFONT].value);
    NXZoneFree(agentZone, defaults[INTERFONTSIZE].value);
    defaults[INTERFONT].value = NXCopyStringBufferFromZone([aFont name], agentZone);
    DEBUG_ASSERT(defaults[INTERFONT].value!=NULL)
    sprintf(buffer, "%f", [aFont pointSize]);
    defaults[INTERFONTSIZE].value = NXCopyStringBufferFromZone(buffer, agentZone);
    DEBUG_ASSERT(defaults[INTERFONTSIZE].value!=NULL)
    NXWriteDefaults([NXApp appName], defaults);
    return self;
}

- (char *)interFont
{
DEBUG_FUNC1(DEBUGLEVEL);
    return defaults[INTERFONT].value;
}

- (float)interFontSize
{
DEBUG_FUNC1(DEBUGLEVEL);
    return atof(defaults[INTERFONTSIZE].value);
}

- getDocOrigin:(NXPoint *)p
{
DEBUG_FUNC1(DEBUGLEVEL);
    sscanf(defaults[DOCPOS].value, "{%f,%f}", &p->x, &p->y);
    return self;
}

- getDocOffset:(NXPoint *)p
{
DEBUG_FUNC1(DEBUGLEVEL);
    sscanf(defaults[DOCOFFSET].value, "{%f,%f}", &p->x, &p->y);
    return self;
}

- (BOOL)reverseVideo
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(REVERSEVIDEO);
}

- (BOOL)useSpecialCursors
{
DEBUG_FUNC1(DEBUGLEVEL);
    return GetBOOLValue(SPECIALCURSORS);
}

- getGraphicsSize:(NXSize *)s
{
DEBUG_FUNC1(DEBUGLEVEL);
    sscanf(defaults[GRAPHICSSIZE].value, "{%f,%f}", &s->width, &s->height);
    return self;
}

- getGraphicsTolerance:(NXSize *)s
{
DEBUG_FUNC1(DEBUGLEVEL);
    sscanf(defaults[SIZETOLERANCE].value, "{%f,%f}", &s->width, &s->height);
    return self;
}

@end

@implementation PrefAgent (Private)

- _revertDefaults
{
DEBUG_FUNC1(DEBUGLEVEL);
    [options1Matrix setState:[self evaluateAll] at:EVALALLAT :0];
    [options1Matrix setState:[self commentOutput] at:COMMMENTOUTAT :0];
    [options1Matrix setState:[self insertErrors] at:INSERTERRSAT :0];
    [options1Matrix setState:[self transcriptMode] at:TRANSCRIPTAT :0];
    [options1Matrix setState:[self autoScroll] at:AUTOSCROLLAT :0];
    [options1Matrix setState:[self confirmOnClose] at:CONFCLOSEAT :0];
    [options1Matrix setState:[self confirmOnQuit] at:CONFQUITAT :0];
    [options1Matrix setState:[self keepBackupCopy] at:KEEPBACKUPAT :0];
    [onErrorMatrix setState:YES at:[self errorAbort] :0];
    [options2Matrix setState:[self dblClickMatch] at:DBLCLICKAT :0];
    [options2Matrix setState:[self autoindent] at:AUTOINDENTAT :0];
    [options2Matrix setState:[self matchParens] at:MATCHPARENAT :0];
    [options2Matrix setState:[self formatAfterPaste] at:FORMATPASTEAT :0];
    [dialectMatrix setState:YES at:[self schemeDialect] :0];
    [self _setDialectOpts:[self schemeDialect]];
    [self _specialFormOpts:-1];
    return self;
}

- _setDefaults
{
    int i,sizetot;

DEBUG_FUNC1(DEBUGLEVEL);
    DEBUG_ASSERT(defaults!=NULL)
    NXZoneFree(agentZone, defaults[EVALUATEALL].value);
    NXZoneFree(agentZone, defaults[COMMENTOUTPUT].value);
    NXZoneFree(agentZone, defaults[INSERTERRORS].value);
    NXZoneFree(agentZone, defaults[TRANSCRIPT].value);
    NXZoneFree(agentZone, defaults[AUTOSCROLL].value);
    NXZoneFree(agentZone, defaults[CONFIRMONCLOSE].value);
    NXZoneFree(agentZone, defaults[CONFIRMONQUIT].value);
    NXZoneFree(agentZone, defaults[KEEPBACKUPCOPY].value);
    defaults[EVALUATEALL].value = NXCopyStringBufferFromZone(([[options1Matrix cellAt:EVALALLAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[EVALUATEALL].value!=NULL)&&(*defaults[EVALUATEALL].value))
    if ([[options1Matrix cellAt:EVALALLAT :0] state])
      [[[[[NXApp mainMenu] findCellWithTag:MENU_ACTIONS] target] findCellWithTag:MENU_ACTIONS_EvalAll] setEnabled:YES];
    else
      [[[[[NXApp mainMenu] findCellWithTag:MENU_ACTIONS] target] findCellWithTag:MENU_ACTIONS_EvalAll] setEnabled:NO];
    defaults[COMMENTOUTPUT].value = NXCopyStringBufferFromZone(([[options1Matrix cellAt:COMMMENTOUTAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[COMMENTOUTPUT].value!=NULL)&&(*defaults[COMMENTOUTPUT].value))
    defaults[INSERTERRORS].value = NXCopyStringBufferFromZone(([[options1Matrix cellAt:INSERTERRSAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[INSERTERRORS].value!=NULL)&&(*defaults[INSERTERRORS].value))
    defaults[TRANSCRIPT].value = NXCopyStringBufferFromZone(([[options1Matrix cellAt:TRANSCRIPTAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[TRANSCRIPT].value!=NULL)&&(*defaults[TRANSCRIPT].value))
    defaults[AUTOSCROLL].value = NXCopyStringBufferFromZone(([[options1Matrix cellAt:AUTOSCROLLAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[AUTOSCROLL].value!=NULL)&&(*defaults[AUTOSCROLL].value))
    defaults[CONFIRMONCLOSE].value = NXCopyStringBufferFromZone(([[options1Matrix cellAt:CONFCLOSEAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[CONFIRMONCLOSE].value!=NULL)&&(*defaults[CONFIRMONCLOSE].value))
    defaults[CONFIRMONQUIT].value = NXCopyStringBufferFromZone(([[options1Matrix cellAt:CONFQUITAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[CONFIRMONQUIT].value!=NULL)&&(*defaults[CONFIRMONQUIT].value))
    defaults[KEEPBACKUPCOPY].value = NXCopyStringBufferFromZone(([[options1Matrix cellAt:KEEPBACKUPAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[KEEPBACKUPCOPY].value!=NULL)&&(*defaults[KEEPBACKUPCOPY].value))
    sprintf(defaults[ERRORABORT].value, "%i", [[onErrorMatrix selectedCell] tag]);
    DEBUG_ASSERT((defaults[ERRORABORT].value!=NULL)&&(*defaults[ERRORABORT].value))
    NXZoneFree(agentZone, defaults[DBLCLICKMATCH].value);
    NXZoneFree(agentZone, defaults[AUTOINDENT].value);
    NXZoneFree(agentZone, defaults[MATCHPARENS].value);
    NXZoneFree(agentZone, defaults[FRMTAFTRPASTE].value);
    defaults[DBLCLICKMATCH].value = NXCopyStringBufferFromZone(([[options2Matrix cellAt:DBLCLICKAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[DBLCLICKMATCH].value!=NULL)&&(*defaults[DBLCLICKMATCH].value))
    defaults[AUTOINDENT].value = NXCopyStringBufferFromZone(([[options2Matrix cellAt:AUTOINDENTAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[AUTOINDENT].value!=NULL)&&(*defaults[AUTOINDENT].value))
    defaults[MATCHPARENS].value = NXCopyStringBufferFromZone(([[options2Matrix cellAt:MATCHPARENAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[MATCHPARENS].value!=NULL)&&(*defaults[MATCHPARENS].value))
    defaults[FRMTAFTRPASTE].value = NXCopyStringBufferFromZone(([[options2Matrix cellAt:FORMATPASTEAT :0] state]?"YES":"NO"), agentZone);
    DEBUG_ASSERT((defaults[FRMTAFTRPASTE].value!=NULL)&&(*defaults[FRMTAFTRPASTE].value))
    sprintf(defaults[SCHEMEDIALECT].value, "%i", [[dialectMatrix selectedCell] tag]);
    DEBUG_ASSERT((defaults[SCHEMEDIALECT].value!=NULL)&&(*defaults[SCHEMEDIALECT].value))
    switch ([[dialectMatrix selectedCell] tag])
      {
        case SICPDIALECT: NXZoneFree(agentZone, defaults[SICPPARAMETERS].value);
                          defaults[SICPPARAMETERS].value = NXCopyStringBufferFromZone([parametersField stringValue], agentZone);
			  DEBUG_ASSERT((defaults[SICPPARAMETERS].value!=NULL)&&(*defaults[SICPPARAMETERS].value))
			  break;
        case MITDIALECT    :
        case MITDIALECTCOMP:NXZoneFree(agentZone, defaults[MITPARAMETERS].value);
                            defaults[MITPARAMETERS].value = NXCopyStringBufferFromZone([parametersField stringValue], agentZone);
			    DEBUG_ASSERT((defaults[MITPARAMETERS].value!=NULL)&&(*defaults[MITPARAMETERS].value))
			    break;
        case OTHERDIALECT:NXZoneFree(agentZone, defaults[EXECUTABLE].value);
 			  NXZoneFree(agentZone, defaults[PARAMETERS].value);
			  defaults[EXECUTABLE].value = NXCopyStringBufferFromZone([executableForm stringValue], agentZone);
			  DEBUG_ASSERT((defaults[EXECUTABLE].value!=NULL)&&(*defaults[EXECUTABLE].value))
			  defaults[PARAMETERS].value = NXCopyStringBufferFromZone([parametersField stringValue], agentZone);
			  DEBUG_ASSERT((defaults[PARAMETERS].value!=NULL)&&(*defaults[PARAMETERS].value))
      }
    NXZoneFree(agentZone, defaults[SPECIALFORMS].value);
    defaults[SPECIALFORMS].value = NXCopyStringBufferFromZone("", agentZone);
    DEBUG_ASSERT((defaults[SPECIALFORMS].value!=NULL)&&(!*defaults[SPECIALFORMS].value))
    sizetot = strlen(defaults[SPECIALFORMS].value)+1;
    [[oldKeywordList freeObjects] free];
    oldKeywordList = keywordList;
    keywordList = [self _copyKeywordList];
    for(i=0; i<[keywordList count]; i++)
      {
        char temp[128];
        sprintf(temp, "{%s,%i}", [[keywordList objectAt:i] keyword], [[keywordList objectAt:i] distinguishedElems]);
	defaults[SPECIALFORMS].value = (char *)NXZoneRealloc(agentZone, defaults[SPECIALFORMS].value, (sizetot+=strlen(temp)+1));
        DEBUG_ASSERT(defaults[SPECIALFORMS].value!=NULL)
	strcat(defaults[SPECIALFORMS].value, temp);
      }
    NXWriteDefaults([NXApp appName], defaults);
    keywordListVersion++;
    return self;
}

- _setDialectOpts:(int)dialect
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (dialect<OTHERDIALECT)
      {
        [executableForm setEnabled:NO];
        [executableForm setStringValue:""];
        if (dialect==SICPDIALECT)
          [parametersField setStringValue:defaults[SICPPARAMETERS].value];
        else
          [parametersField setStringValue:defaults[MITPARAMETERS].value];
        [parametersBox setTitle:ADDCMDLINEPARAMS];
      }
    else
      {
        [executableForm setEnabled:YES];
        [executableForm setStringValue:defaults[EXECUTABLE].value];
        [parametersField setStringValue:defaults[PARAMETERS].value];
        [parametersBox setTitle:CMDLINEPARAMS];
      }
    [parametersBox display];
    return self;
}

- _specialFormOpts:(int)buttontag
{
    id matrix=[formsBrowser matrixInColumn:0];
    int rowNum=[matrix selectedRow];

DEBUG_FUNC1(DEBUGLEVEL);
    switch (buttontag)
      {
        case -1: [formsField setStringValue:NULL];
                 [deField setStringValue:NULL];
		 [[keywordList freeObjects] free];
		 keywordList = oldKeywordList;
		 oldKeywordList = [self _copyKeywordList];
                 [formsBrowser loadColumnZero];
		 return self;
	case 0 : if (rowNum<0) break;
	         if (NXOrderStrings((unsigned char *)[formsField stringValue],(unsigned char *)[[keywordList objectAt:rowNum] keyword],NO,-1,NULL))
		   {
		     [[keywordList objectAt:rowNum] setKeyword:(char *)[formsField stringValue]];
		     [formsBrowser reloadColumn:0];
		   }
                 if ([deField intValue]!=[[keywordList objectAt:rowNum] distinguishedElems])
	           [[keywordList objectAt:rowNum] setDistElems:[deField intValue]];
                 [matrix selectCellAt:rowNum :0];
		 [self formsBrowserClick:self];
		 return self;
	case 1 : if (![self isSpecialForm:[formsField stringValue]])
		   {
		     [keywordList addObject:[[Keyword allocFromZone:agentZone] initTo:(char *)[formsField stringValue] with:[deField intValue]]];
                     [formsBrowser reloadColumn:0];
                     [matrix selectCellAt:[matrix cellCount]-1 :0];
		     [self formsBrowserClick:self];
		   }
	         return self;
	case 2 : if (rowNum<0) break;
	         [[keywordList removeObjectAt:rowNum] free];
                 [deField setStringValue:""];
                 [[formsField setStringValue:"" at:0] selectTextAt:0];
                 [formsBrowser reloadColumn:0];
		 return self;
      }
    return self;
}

- _copyKeywordList
{
    int i;
    id list;
    list = [[List alloc] init];
    for(i=0; i<[keywordList count]; i++)
      [list addObject:[[keywordList objectAt:i] copy]];
    return list;
}

@end

@implementation PrefAgent (FormsBrowser)

- (int)browser:sender fillMatrix:matrix inColumn:(int)column
{
    int i;

DEBUG_FUNC1(DEBUGLEVEL);
    for(i=0; i<[keywordList count]; i--)
      {
        [matrix addRow];
	[[matrix cellAt:i :0] setTag:i];
      }
    return [keywordList count];
}

- browser:sender loadCell:aCell atRow:(int)row inColumn:(int)column
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (defaults[SPECIALFORMS].value)
      {
        [aCell setStringValueNoCopy:[[keywordList objectAt:row] keyword]];
        DEBUG_ASSERT(!NXOrderStrings((unsigned char *)[aCell stringValue], (unsigned char *)[[keywordList objectAt:row] keyword], YES, -1, NULL))
        [aCell setLeaf:YES];
        [aCell setLoaded:YES];
      }
    return self;
}

@end

@implementation Keyword

- init
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [self initTo:"" with:-1];
}

- initTo:(char *)aString with:(int)anInt
{
DEBUG_FUNC1(DEBUGLEVEL);
    [super init];
    keyword = NXCopyStringBufferFromZone(aString,[self zone]);
    distelems = anInt;
    return self;
}

- copy
{
    return [[Keyword allocFromZone:[self zone]] initTo:keyword with:distelems];
}

- free
{
    free(keyword);
    return [super free];
}

- (char *)keyword
{
DEBUG_FUNC1(DEBUGLEVEL);
    return keyword;
}

- (int)distinguishedElems
{
DEBUG_FUNC1(DEBUGLEVEL);
    return distelems;
}

- setKeyword:(char *)aString
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (keyword!=NULL) free(keyword);
    keyword = NXCopyStringBufferFromZone(aString,[self zone]);
    DEBUG_ASSERT((keyword!=NULL)&&(*keyword))
    return self;
}

- setDistElems:(int)anInt
{
DEBUG_FUNC1(DEBUGLEVEL);
    distelems = anInt;
    return self;
}

@end
